package com.imooc.controller;

import com.alibaba.cloud.commons.lang.StringUtils;
import com.google.gson.Gson;
import com.imooc.async.AsyncTask;
import com.imooc.base.BaseInfoProperties;
import com.imooc.bo.RegistLoginBO;
import com.imooc.mq.RabbitMQSMSConfig;
import com.imooc.mq.SMSContentQO;
import com.imooc.pojo.Users;
import com.imooc.result.GraceJSONResult;
import com.imooc.result.ResponseStatusEnum;
import com.imooc.retry.RetryComponent;
import com.imooc.service.UsersService;
import com.imooc.utils.IPUtil;
import com.imooc.utils.JwtUtils;
import com.imooc.vo.UsersVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.ReturnedMessage;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.util.concurrent.TimeUnit;

/**
 * 提供给app端使用
 * @author liansen
 * @create 12-21-11:14
 */
@Slf4j
@RestController
@RequestMapping("/passport")
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class PassPortController extends BaseInfoProperties {

    @Autowired
    private JwtUtils jwtUtils;

    @Autowired
    private RetryComponent retryComponent;

    @Autowired
    private AsyncTask asyncTask;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private UsersService usersService;

    @PostMapping("/getSMSCode")
    public GraceJSONResult getSMSCode(@RequestParam("mobile") String mobile, HttpServletRequest request){

        if(StringUtils.isBlank(mobile)){
            return GraceJSONResult.errorMsg("手机号不能为空");
        }

        String code = (int) ((Math.random()*9+1) * 100000) + "";

//        重试
//        String code = retryComponent.sendSmsWithRetry();
        /**
         * String exchange, String routingKey, Object message
         */

        //使用消息队列异步解耦发送短信
        SMSContentQO content = new SMSContentQO();
        content.setMobile(mobile);
        content.setContent(code);
        String bodyJson = new Gson().toJson(content);

        //发布消息到交换器后会不管成不成功都会触发的回调方法
        rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
            /**
             * @param correlationData 当前消息的唯一关联数据(消息的唯一id)
             * @param ack 交换机是否成功接收到消息
             * @param cause 失败的原因
             */
            @Override
            public void confirm(CorrelationData correlationData, boolean ack, String cause) {
                log.info("ack:" + ack + "消息进入交换机-进入回调方法");
                log.info("correlationData:" + correlationData);
                if(ack){
                    //成功的话,输出cause为null
                    log.info("交换机成功接收到消息~{}", cause);

                }else{
                    log.info("交换机接收到消息失败, 失败原因~{}", cause);
//                    rabbitTemplate.convertAndSend(RabbitMQSMSConfig.SMS_EXCHANGE,
//                            RabbitMQSMSConfig.ROUTING_KEY_SMS_SEND_LOGIN,
//                            bodyJson,
//                            new CorrelationData());
                }
            }
        });

        //消息从交换机到队列的过程中，接收失败后的回调方法
        rabbitTemplate.setReturnsCallback(new RabbitTemplate.ReturnsCallback() {
            @Override
            public void returnedMessage(ReturnedMessage returned) {
                log.info("没有设置强制委托, 但是走了回调函数");
                log.info("队列接收消息失败, 进入returnsCallback回调方法");
                log.info("exchange:" + returned.getExchange());
                log.info("replyText:" + returned.getReplyText());
                log.info("routingKey:" + returned.getRoutingKey());
                log.info("ReplyCode:" + returned.getReplyCode());
            }
        });

        //消息属性处理的类对象(当前需要对ttl属性进行设置)
//        MessagePostProcessor message = new MessagePostProcessor() {
//            @Override
//            public Message postProcessMessage(Message message) throws AmqpException {
//                //设置过期时间为10s,如果10s内该消息未被消费者接收，则会被队列抛弃
//                message.getMessageProperties().setExpiration(String.valueOf(10*1000));
//                return message;
//            }
//        };

        rabbitTemplate.convertAndSend(RabbitMQSMSConfig.SMS_EXCHANGE,
                RabbitMQSMSConfig.ROUTING_KEY_SMS_SEND_LOGIN,
                bodyJson,
                new CorrelationData());  // new CorrelationData()的构造函数就是一个uuid

//        for(int i=0; i<10; i++){
//            rabbitTemplate.convertAndSend(RabbitMQSMSConfig.SMS_EXCHANGE,
//                    RabbitMQSMSConfig.ROUTING_KEY_SMS_SEND_LOGIN,
//                    bodyJson,
//                    new CorrelationData());  // new CorrelationData()的构造函数就是一个uuid
//        }

        //异步加重试获取短信验证码, 这里异步返回void或者Future,
//        String code = asyncTask.SmsTask();

        //获取用户ip
        String userIp = IPUtil.getRequestIp(request);
        /**
         * setnx是redis命令, setIfAbsent是java中的命令,
         * 意思都是: 如果为空就set值，并返回1;如果存在(不为空)不进行操作，并返回0;
         * 这里意思就是如果redis中有userIp这个key,就不做任何事，如果没有再set;
         *
         * 这里用setnx的原因是,如果用户用多个手机同时发送请求，都过了拦截器，到这里后，只能允许
         * 一台手机操作成功；
         */
        redisTemplate.opsForValue().setIfAbsent(MOBILE_SMSCODE + ":" + userIp, mobile, 60, TimeUnit.SECONDS);

        //将手机号作为key, code作为值存入redis，设置过期时间为半小时; 用于之后的登录验证
        redisTemplate.opsForValue().set(MOBILE_SMSCODE + ":" + mobile, code, 30*60, TimeUnit.SECONDS);
        return GraceJSONResult.ok();
    }


    /**
     * 一键注册登录
     * @param registLoginBO
     * @return
     */
    @PostMapping("/login")
    public GraceJSONResult login(@RequestBody @Validated RegistLoginBO registLoginBO){
        String mobile = registLoginBO.getMobile();
        String smsCode = registLoginBO.getSmsCode();
        //判断用户传来的验证码是否和redis中的一致
        String redisCode = redisTemplate.opsForValue().get(MOBILE_SMSCODE + ":" + mobile);
        if(StringUtils.isBlank(redisCode) || !smsCode.equals(redisCode)){
            return GraceJSONResult.errorCustom(ResponseStatusEnum.SMS_CODE_ERROR);
        }
        //根据手机号判断该用户是否存在于数据库中，如果不存在，就注册；如果存在，就登录
        Users user = usersService.userIsExistsByMobile(mobile);
        if(user == null){
            //注册信息入库
//            user = usersService.createUser(mobile);
            user = usersService.createUserAndInitResumeMQ(mobile);
        }
//        String userId = user.getId();
        //生成token，存在redis中  分布式会话
//        String uToken = UUID.randomUUID().toString();
//        redisTemplate.opsForValue().set(TOKEN_USER_PREFIX + SYMBOL_DOT +  REDIS_USER_TOKEN + ":" + userId, uToken);

        String userJsonString = new Gson().toJson(user);

        //设置手机端jwt过期时间为3小时
        String uToken = jwtUtils.createJWTWithPrefix(userJsonString, 3 * 3600 * 1000L, TOKEN_USER_PREFIX);

//        jwtUtils.createJWTWithPrefix();
        //注册登录成功后，将redis中的验证码删掉，将用户信息和token返回给前端
        redisTemplate.delete(MOBILE_SMSCODE + ":" + mobile);
        UsersVO usersVO = new UsersVO();
        BeanUtils.copyProperties(user, usersVO);
        usersVO.setUserToken(uToken);
        return GraceJSONResult.ok(usersVO);
    }

    //退出登录
    @PostMapping("/logout")
    public GraceJSONResult login(@RequestParam("userId") String userId){
        //从redis中删掉token即可
//        redisTemplate.delete(TOKEN_USER_PREFIX + SYMBOL_DOT + REDIS_USER_TOKEN + ":" + userId);
        return GraceJSONResult.ok();
    }
}